/** * */ package com.erakk.lnreader.dao; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.preference.PreferenceManager; import android.util.Log; import com.erakk.lnreader.AlternativeLanguageInfo; import com.erakk.lnreader.Constants; import com.erakk.lnreader.LNReaderApplication; import com.erakk.lnreader.R; import com.erakk.lnreader.UIHelper; import com.erakk.lnreader.callback.CallbackEventData; import com.erakk.lnreader.callback.ICallbackNotifier; import com.erakk.lnreader.helper.BakaReaderException; import com.erakk.lnreader.helper.DBHelper; import com.erakk.lnreader.helper.Util; import com.erakk.lnreader.helper.db.BookModelHelper; import com.erakk.lnreader.helper.db.BookmarkModelHelper; import com.erakk.lnreader.helper.db.FindMissingModelHelper; import com.erakk.lnreader.helper.db.ImageModelHelper; import com.erakk.lnreader.helper.db.NovelCollectionModelHelper; import com.erakk.lnreader.helper.db.NovelContentModelHelper; import com.erakk.lnreader.helper.db.NovelContentUserHelperModel; import com.erakk.lnreader.helper.db.PageCategoriesHelper; import com.erakk.lnreader.helper.db.PageModelHelper; import com.erakk.lnreader.helper.db.UpdateInfoModelHelper; import com.erakk.lnreader.model.BookModel; import com.erakk.lnreader.model.BookmarkModel; import com.erakk.lnreader.model.FindMissingModel; import com.erakk.lnreader.model.ImageModel; import com.erakk.lnreader.model.NovelCollectionModel; import com.erakk.lnreader.model.NovelContentModel; import com.erakk.lnreader.model.NovelContentUserModel; import com.erakk.lnreader.model.PageModel; import com.erakk.lnreader.model.UpdateInfoModel; import com.erakk.lnreader.parser.BakaTsukiParser; import com.erakk.lnreader.parser.BakaTsukiParserAlternative; import com.erakk.lnreader.parser.CommonParser; import com.erakk.lnreader.task.DownloadFileTask; import org.jsoup.Connection.Response; import org.jsoup.HttpStatusException; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import java.io.EOFException; import java.io.File; import java.io.IOException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.Locale; import java.util.Map; /** * @author Nandaka */ public class NovelsDao { private static final String TAG = NovelsDao.class.toString(); private static final DBHelper dbh; private static final Context context; private static final NovelsDao instance; static { context = LNReaderApplication.getInstance().getApplicationContext(); dbh = new DBHelper(context); instance = new NovelsDao(); } public static NovelsDao getInstance() { return instance; } private NovelsDao() { } // region db // public DBHelper getDBHelper() { // return dbh; // } public void deleteDB() { synchronized (dbh) { SQLiteDatabase db = dbh.getWritableDatabase(); try { dbh.deletePagesDB(db); } finally { db.close(); } } } public String copyDB(boolean makeBackup, String filename) throws IOException { synchronized (dbh) { String filePath; filePath = dbh.copyDB(context, makeBackup, filename); return filePath; } } public String checkDB() { synchronized (dbh) { SQLiteDatabase db = dbh.getWritableDatabase(); return dbh.checkDB(db); } } // public void temp() { // synchronized (dbh) { // SQLiteDatabase db = dbh.getWritableDatabase(); // db.execSQL("DROP TABLE IF EXISTS " + dbh.TABLE_NOVEL_BOOKMARK); // db.execSQL(dbh.DATABASE_CREATE_NOVEL_BOOKMARK); // } // } // endregion // region Novel Listing related public ArrayList<PageModel> getNovels(ICallbackNotifier notifier, boolean alphOrder) throws Exception { return getNovelHelper(notifier, Constants.ROOT_NOVEL_ENGLISH, null, alphOrder); } public ArrayList<PageModel> getNovelsFromInternet(ICallbackNotifier notifier) throws Exception { checkInternetConnection(); if (notifier != null) { String message = context.getResources().getString(R.string.load_novel_list_download); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } return getNovelsHelperFromInternet(notifier, Constants.ROOT_NOVEL_ENGLISH, null); } public ArrayList<PageModel> getWatchedNovel() { ArrayList<PageModel> watchedNovel = null; synchronized (dbh) { SQLiteDatabase db = null; try { long start = java.lang.System.currentTimeMillis(); db = dbh.getReadableDatabase(); watchedNovel = dbh.getAllWatchedNovel(db, true, isQuickLoad()); Log.i(TAG, String.format("DB Loading Time - Watched Novel: %s", java.lang.System.currentTimeMillis() - start)); } finally { if (db != null) db.close(); } } return watchedNovel; } public ArrayList<PageModel> getTeaser(ICallbackNotifier notifier, boolean alphOrder) throws Exception { return getNovelHelper(notifier, Constants.ROOT_TEASER, Constants.STATUS_TEASER, alphOrder); } public ArrayList<PageModel> getTeaserFromInternet(ICallbackNotifier notifier) throws Exception { checkInternetConnection(); if (notifier != null) { String message = context.getResources().getString(R.string.load_novel_list_download); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } return getNovelsHelperFromInternet(notifier, Constants.ROOT_TEASER, Constants.STATUS_TEASER); } public ArrayList<PageModel> getWebNovel(ICallbackNotifier notifier, boolean alphOrder) throws Exception { return getNovelHelper(notifier, Constants.ROOT_WEB, Constants.STATUS_WEB, alphOrder); } public ArrayList<PageModel> getWebNovelFromInternet(ICallbackNotifier notifier) throws Exception { checkInternetConnection(); if (notifier != null) { String message = context.getResources().getString(R.string.load_novel_list_download); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } return getNovelsHelperFromInternet(notifier, Constants.ROOT_WEB, Constants.STATUS_WEB); } public ArrayList<PageModel> getOriginal(ICallbackNotifier notifier, boolean alphOrder) throws Exception { return getNovelHelper(notifier, Constants.ROOT_ORIGINAL, Constants.STATUS_ORIGINAL, alphOrder); } public ArrayList<PageModel> getOriginalFromInternet(ICallbackNotifier notifier) throws Exception { checkInternetConnection(); if (notifier != null) { String message = context.getResources().getString(R.string.load_novel_list_download); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } return getNovelsHelperFromInternet(notifier, Constants.ROOT_ORIGINAL, Constants.STATUS_ORIGINAL); } public ArrayList<PageModel> getAlternative(ICallbackNotifier notifier, boolean alphOrder, String language) throws Exception { SQLiteDatabase db = null; PageModel page = null; ArrayList<PageModel> list = null; synchronized (dbh) { try { long start = java.lang.System.currentTimeMillis(); db = dbh.getReadableDatabase(); page = PageModelHelper.getAlternativePage(dbh, db, language); if (page != null) { list = dbh.getAllAlternative(db, alphOrder, isQuickLoad(), language); Log.d(TAG, "Found: " + list.size()); } Log.i(TAG, String.format("DB Loading Time - Alt Novel %s: %s", language, java.lang.System.currentTimeMillis() - start)); } finally { if (db != null) db.close(); } } if (page == null) { return getAlternativeFromInternet(notifier, language); } return list; } /** * Due to several changes in baka-tsuki pages as April 2014, crawling recursively through all subcategories * * @param notifier * @param language * @return * @throws Exception */ public ArrayList<PageModel> getAlternativeFromInternet(final ICallbackNotifier notifier, String language) throws Exception { checkInternetConnection(); if (notifier != null) { String message = context.getResources().getString(R.string.load_novel_list_alternate, language); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } // parse Information PageModel modelPage = new PageModel(); if (language != null) { modelPage.setPage(AlternativeLanguageInfo.getAlternativeLanguageInfo().get(language).getCategoryInfo()); modelPage.setTitle(context.getResources().getString(R.string.title_novels_page_alternate, language)); modelPage.setLanguage(language); } modelPage = getPageModel(modelPage, notifier); modelPage.setType(PageModel.TYPE_NOVEL); // update page model synchronized (dbh) { SQLiteDatabase db = dbh.getWritableDatabase(); modelPage = PageModelHelper.insertOrUpdatePageModel(dbh, db, modelPage, true); Log.d(TAG, "Updated " + language); } // get all sub-categories final ArrayList<Document> global_docs = new ArrayList<Document>(); // list of all documents ArrayList<String> links = null; String url = null; if (language != null) url = UIHelper.getBaseUrl(LNReaderApplication.getInstance().getApplicationContext()) + "/project/index.php?title=" + AlternativeLanguageInfo.getAlternativeLanguageInfo().get(language).getCategoryInfo(); int retry = 0; while (retry < getRetry()) { try { Response response = connect(url, retry); Document doc = response.parse(); global_docs.add(doc); // add main category page to global_docs links = BakaTsukiParserAlternative.CrawlAlternativeCategory(doc); Log.d(TAG, "Found from internet: " + links.size() + " Sub-Category"); if (notifier != null) { String message = context.getResources().getString(R.string.load_subcategory_list_finished, links.size()); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } break; } catch (EOFException eof) { ++retry; if (notifier != null) { String message = context.getResources().getString(R.string.load_novel_retry, retry, getRetry(), eof.getMessage()); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } if (retry >= getRetry()) throw eof; } catch (IOException eof) { ++retry; String message = context.getResources().getString(R.string.load_novel_retry, retry, getRetry(), eof.getMessage()); if (notifier != null) { notifier.onProgressCallback(new CallbackEventData(message, TAG)); } Log.d(TAG, message, eof); if (retry >= getRetry()) throw eof; } } // get all documents from pre-defined links final ArrayList<String> getLinks = new ArrayList<String>(links); final Object sync_lock = new Object(); // locking's object for global_docs final Object sync_lock_log = new Object(); // locking's object for error message // Since there're only about 1 to 4 sub-categories, we'll create all threads at once if (links != null) { Thread[] threads = null; if (getLinks.size() > 0) threads = new Thread[getLinks.size()]; for (int i = 0; i < getLinks.size(); i++) { final int access_index = i; threads[access_index] = new Thread() { @Override public void run() { int retry = 0; while (retry < getRetry()) { try { String url = UIHelper.getBaseUrl(LNReaderApplication.getInstance().getApplicationContext()) + "/project/index.php?title=" + getLinks.get(access_index); if (url != null) { Response response = connect(url, retry); Document doc = response.parse(); synchronized (sync_lock) { global_docs.add(doc); // add main category page to global_docs Log.d(TAG, "Found from internet: " + url + " page."); } } break; } catch (EOFException eof) { ++retry; synchronized (sync_lock_log) { if (retry >= getRetry()) Log.d(TAG, "Timeout when accessing " + getLinks.get(access_index)); } } catch (IOException eof) { ++retry; synchronized (sync_lock_log) { if (retry >= getRetry()) Log.d(TAG, "Timeout when accessing " + getLinks.get(access_index)); } } } } }; threads[access_index].start(); // start threads } if (getLinks.size() != 0) { for (int i = 0; i < getLinks.size(); i++) threads[i].join(); } } Log.d(TAG, "Number of global_docs found: " + global_docs.size()); // parse all documents (ensure no double entities) ArrayList<PageModel> list = new ArrayList<PageModel>(); for (Document current_doc : global_docs) { ArrayList<PageModel> temp_list = BakaTsukiParserAlternative.ParseAlternativeList(current_doc, language); for (PageModel current_list : temp_list) { boolean isExist = false; for (PageModel pointed_list : list) { if (pointed_list.getPage().equals(current_list.getPage())) { isExist = true; break; } } if (!isExist) list.add(current_list); } } // sorts alphabetically Collections.sort(list, new Comparator<PageModel>() { @Override public int compare(PageModel p1, PageModel p2) { return p1.getPage().compareTo(p2.getPage()); } }); // set orderings and last update list = getUpdateInfo(list, notifier); Date dt = new Date(); for (int i = 0; i < list.size(); i++) { PageModel p = list.get(i); p.setOrder(i); p.setLastCheck(dt); } // get mediawiki page info getUpdateInfo(list, notifier); // save lists synchronized (dbh) { SQLiteDatabase db = dbh.getWritableDatabase(); for (PageModel pageModel : list) { pageModel = PageModelHelper.insertOrUpdatePageModel(dbh, db, pageModel, true); Log.d(TAG, "Updated " + language + " novel: " + pageModel.getPage()); } } return list; } /** * Get Novel list from db. If not exists, get it from internet * * @param notifier * @param parent * @param alphOrder * @param status * @return * @throws Exception */ private ArrayList<PageModel> getNovelHelper(ICallbackNotifier notifier, final String parent, final String status, boolean alphOrder) throws Exception { SQLiteDatabase db = null; ArrayList<PageModel> list = null; synchronized (dbh) { try { long start = java.lang.System.currentTimeMillis(); db = dbh.getReadableDatabase(); list = dbh.getAllNovelsByCategory(db, alphOrder, isQuickLoad(), new String[]{parent}); Log.d(TAG, "Found: " + list.size()); Log.i(TAG, String.format("DB Loading Time - %s: %s", parent, java.lang.System.currentTimeMillis() - start)); } finally { if (db != null) db.close(); } } if (parent == null) { return getNovelsHelperFromInternet(notifier, parent, status); } return list; } /** * Get novel list from Internet based on given parent page, * e.g.: * - "Category:Teaser" * - "Category:Original_novel" * - "Category:Light_novel_(English)" * Please define the parent page in Constants. * * @param notifier * @param parent * @param status * @return * @throws Exception * @throws EOFException * @throws IOException */ private ArrayList<PageModel> getNovelsHelperFromInternet(ICallbackNotifier notifier, final String parent, final String status) throws Exception, EOFException, IOException { Date date = new Date(); PageModel parentPage = new PageModel(); parentPage.setPage(parent); parentPage.setLanguage(Constants.LANG_ENGLISH); parentPage.setTitle(parent); parentPage = getPageModel(parentPage, notifier); parentPage.setType(PageModel.TYPE_OTHER); parentPage.setLastCheck(date); // update page model synchronized (dbh) { SQLiteDatabase db = dbh.getWritableDatabase(); parentPage = PageModelHelper.insertOrUpdatePageModel(dbh, db, parentPage, true); Log.d(TAG, "Updated " + parent); } // get list String url = UIHelper.getBaseUrl(LNReaderApplication.getInstance().getApplicationContext()) + "/project/index.php?title=" + parent; Log.d(TAG, "Url: " + url); ArrayList<PageModel> list = null; int retry = 0; while (retry < getRetry()) { try { Response response = connect(url, retry); Document doc = response.parse(); list = BakaTsukiParser.parseGenericNovelList(doc, parent, status); for (PageModel pageModel : list) { pageModel.setParent(parent); pageModel.setParentPageModel(parentPage); } Log.d(TAG, "Found from internet: " + list.size() + " for " + parent); list = getUpdateInfo(list, notifier); for (PageModel pageModel : list) { pageModel.setLastCheck(date); } if (notifier != null) { String message = context.getResources().getString(R.string.load_novel_list_finished, list.size()); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } break; } catch (EOFException eof) { ++retry; if (notifier != null) { String message = context.getResources().getString(R.string.load_novel_retry, retry, getRetry(), eof.getMessage()); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } if (retry >= getRetry()) throw eof; } catch (IOException eof) { ++retry; String message = context.getResources().getString(R.string.load_novel_retry, retry, getRetry(), eof.getMessage()); if (notifier != null) { notifier.onProgressCallback(new CallbackEventData(message, TAG)); } Log.d(TAG, message, eof); if (retry >= getRetry()) throw eof; } } // save list synchronized (dbh) { SQLiteDatabase db = dbh.getWritableDatabase(); for (PageModel pageModel : list) { pageModel = PageModelHelper.insertOrUpdatePageModel(dbh, db, pageModel, true); Log.d(TAG, "Updated: " + pageModel.getPage()); } } synchronized (dbh) { SQLiteDatabase db = dbh.getReadableDatabase(); // reload data from db list = dbh.getAllNovelsByCategory(db, isAlphabeticalOrder(), isQuickLoad(), new String[]{parent}); } return list; } public int deleteNovel(PageModel novel) { synchronized (dbh) { SQLiteDatabase db = dbh.getWritableDatabase(); return NovelCollectionModelHelper.deleteNovel(dbh, db, novel); } } // endregion // region PageModel related /** * Get page model from db. If autoDownload = true, get the pageModel from * internet if not exists. * * @param page * @param notifier * @param autoDownload * @return * @throws Exception */ private PageModel getPageModel(PageModel page, ICallbackNotifier notifier, boolean autoDownload) throws Exception { PageModel pageModel = null; synchronized (dbh) { SQLiteDatabase db = dbh.getReadableDatabase(); try { pageModel = PageModelHelper.getPageModel(dbh, db, page.getPage()); } finally { db.close(); } } if (pageModel == null && autoDownload) { pageModel = getPageModelFromInternet(page, notifier); } return pageModel; } /** * Get page model from db. Get the pageModel from internet if not exists. * * @param page * @param notifier * @return * @throws Exception */ public PageModel getPageModel(PageModel page, ICallbackNotifier notifier) throws Exception { return getPageModel(page, notifier, true); } /** * Return pageModel, null if not exist. * * @param page * @param notifier * @return * @throws Exception */ public PageModel getExistingPageModel(PageModel page, ICallbackNotifier notifier) throws Exception { return getPageModel(page, notifier, false); } public PageModel getPageModelFromInternet(PageModel page, ICallbackNotifier notifier) throws Exception { checkInternetConnection(); Log.d(TAG, "PageModel = " + page.getPage()); int retry = 0; while (retry < getRetry()) { try { if (notifier != null) { String message = context.getResources().getString(R.string.load_novel_list_fetch, page.getTitle()); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } String encodedTitle = Util.UrlEncode(page.getPage().trim()); String fullUrl = String.format(Constants.API_URL_INFO, UIHelper.getBaseUrl(LNReaderApplication.getInstance().getApplicationContext()), encodedTitle); Response response = connect(fullUrl, retry); PageModel pageModel = null; String lang = page.getLanguage(); if (lang != null) pageModel = CommonParser.parsePageAPI(page, response.parse(), fullUrl); pageModel.setFinishedRead(page.isFinishedRead()); pageModel.setWatched(page.isWatched()); synchronized (dbh) { // save to db and get saved value SQLiteDatabase db = dbh.getWritableDatabase(); try { pageModel = PageModelHelper.insertOrUpdatePageModel(dbh, db, pageModel, false); } finally { db.close(); } } return pageModel; } catch (EOFException eof) { ++retry; if (notifier != null) { String message = context.getResources().getString(R.string.load_novel_retry, retry, getRetry(), eof.getMessage()); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } if (retry >= getRetry()) throw eof; } catch (IOException eof) { ++retry; String message = context.getResources().getString(R.string.load_novel_retry, retry, getRetry(), eof.getMessage()); if (notifier != null) { notifier.onProgressCallback(new CallbackEventData(message, TAG)); } Log.d(TAG, message, eof); if (retry >= getRetry()) throw eof; } } return null; } public PageModel updatePageModel(PageModel page) { PageModel pageModel = null; synchronized (dbh) { SQLiteDatabase db = dbh.getWritableDatabase(); try { pageModel = PageModelHelper.insertOrUpdatePageModel(dbh, db, page, false); } finally { db.close(); } } return pageModel; } public int deletePage(PageModel page) { synchronized (dbh) { // get from db SQLiteDatabase db = dbh.getReadableDatabase(); try { return PageModelHelper.deletePageModel(dbh, db, page); } finally { db.close(); } } } public PageModel getUpdateInfo(PageModel pageModel, ICallbackNotifier notifier) throws Exception { ArrayList<PageModel> pageModels = new ArrayList<PageModel>(); pageModels.add(pageModel); pageModels = getUpdateInfo(pageModels, notifier); return pageModels.get(0); } /*** * Bulk update page info through wiki API - LastUpdateInfo. - Redirected. - * Missing flag. * * @param pageModels * @param notifier * @return * @throws Exception */ public ArrayList<PageModel> getUpdateInfo(ArrayList<PageModel> pageModels, ICallbackNotifier notifier) throws Exception { ArrayList<PageModel> resultPageModel = new ArrayList<PageModel>(); ArrayList<PageModel> noInfoPageModel = new ArrayList<PageModel>(); ArrayList<PageModel> externalPageModel = new ArrayList<PageModel>(); int i = 0; int pageCounter = 0; int retry = 0; while (i < pageModels.size()) { int apiPageCount = 1; ArrayList<PageModel> checkedPageModel = new ArrayList<PageModel>(); String titles = ""; while (i < pageModels.size() && apiPageCount < 20) { if (pageModels.get(i).isExternal()) { pageModels.get(i).setMissing(false); externalPageModel.add(pageModels.get(i)); ++i; continue; } if (pageModels.get(i).isMissing() || pageModels.get(i).getPage().endsWith("&action=edit&redlink=1")) { pageModels.get(i).setMissing(true); noInfoPageModel.add(pageModels.get(i)); ++i; continue; } if (titles.length() + pageModels.get(i).getPage().length() < Constants.URL_LENGTH_LIMIT) { titles += "|" + Util.UrlEncode(pageModels.get(i).getPage().trim()); checkedPageModel.add(pageModels.get(i)); ++i; ++apiPageCount; } else { break; } } // request the page while (retry < getRetry()) { try { String url = String.format(Constants.API_URL_INFO, UIHelper.getBaseUrl(LNReaderApplication.getInstance().getApplicationContext()), titles); // Log.d(TAG, "Trying to get: " + baseUrl + titles); Response response = connect(url, retry); Document doc = response.parse(); ArrayList<PageModel> updatedPageModels = CommonParser.parsePageAPI(checkedPageModel, doc, url); for (PageModel updatedPageModel : updatedPageModels) { if (updatedPageModel.getCategories() != null && updatedPageModel.getCategories().size() > 0) { SQLiteDatabase db = dbh.getWritableDatabase(); PageCategoriesHelper.insertCategoryByPage(dbh, db, updatedPageModel.getPage(), updatedPageModel.getCategories()); } } resultPageModel.addAll(updatedPageModels); if (notifier != null) { pageCounter += checkedPageModel.size(); String message = context.getResources().getString(R.string.load_novel_chapters_download_info, pageCounter, pageModels.size()); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } break; } catch (EOFException eof) { ++retry; if (notifier != null) { String message = context.getResources().getString(R.string.load_novel_retry, retry, getRetry(), eof.getMessage()); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } if (retry >= getRetry()) throw eof; } catch (IOException eof) { ++retry; String message = context.getResources().getString(R.string.load_novel_retry, retry, getRetry(), eof.getMessage()); if (notifier != null) { notifier.onProgressCallback(new CallbackEventData(message, TAG)); } Log.d(TAG, message, eof); if (retry >= getRetry()) throw eof; } } } resultPageModel.addAll(noInfoPageModel); if (notifier != null) { pageCounter += noInfoPageModel.size(); String message = context.getResources().getString(R.string.load_novel_chapters_download_info, pageCounter, pageModels.size()); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } if (UIHelper.getUpdateIncludeExternal(context)) { for (PageModel page : externalPageModel) { getExternalUpdateInfo(page); if (notifier != null) { pageCounter++; String message = context.getResources().getString(R.string.load_novel_chapters_download_external, pageCounter, pageModels.size()); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } } resultPageModel.addAll(externalPageModel); } return resultPageModel; } public void getExternalUpdateInfo(PageModel page) { int retry; Map<String, String> headers = null; // Date: Wed, 13 Nov 2013 13:08:35 GMT DateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy kk:mm:ss z", Locale.US); retry = 0; while (retry < getRetry()) { try { headers = connect(page.getPage(), retry).headers(); break; } catch (HttpStatusException hex) { if (hex.getStatusCode() == 404) { Log.w(TAG, "Page not Found (404) for: " + page.getPage()); page.setMissing(true); break; } Log.e(TAG, "Error when getting updated date for: " + page.getPage(), hex); } catch (Exception e) { Log.e(TAG, "Error when getting updated date for: " + page.getPage(), e); ++retry; } } if (headers != null) { String dateStr = null; if (headers.containsKey("Last-Modified")) { dateStr = headers.get("Last-Modified"); } else if (headers.containsKey("Date")) { dateStr = headers.get("Date"); } if (!Util.isStringNullOrEmpty(dateStr)) { try { Log.d(TAG, "External Novel last update: " + dateStr); page.setLastUpdate(df.parse(dateStr)); } catch (Exception e) { Log.e(TAG, "Failed to parse date for: " + page.getPage(), e); } } } page.setLastCheck(new Date()); } // endregion // region NovelCollectionModel public NovelCollectionModel getNovelDetails(PageModel page, ICallbackNotifier notifier, boolean autoDownload) throws Exception { NovelCollectionModel novel = null; synchronized (dbh) { SQLiteDatabase db = dbh.getReadableDatabase(); try { novel = NovelCollectionModelHelper.getNovelDetails(dbh, db, page.getPage()); } finally { db.close(); } } if (novel == null && autoDownload) { novel = getNovelDetailsFromInternet(page, notifier); } // get the book chapters for (BookModel book : novel.getBookCollections()) { ArrayList<PageModel> chapters = book.getChapterCollection(); // check if chapter have updates for (PageModel chapter : chapters) { chapter.setUpdated(NovelsDao.getInstance().isContentUpdated(chapter)); } } return novel; } public NovelCollectionModel getNovelDetailsFromInternet(PageModel page, ICallbackNotifier notifier) throws Exception { checkInternetConnection(); Log.d(TAG, "Getting Novel Details from internet: " + page.getPage()); NovelCollectionModel novel = null; int retry = 0; while (retry < getRetry()) { try { if (notifier != null) { String message = context.getResources().getString(R.string.load_novel_details_download, page.getPage()); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } String encodedTitle = Util.UrlEncode(page.getPage()); String fullUrl = UIHelper.getBaseUrl(LNReaderApplication.getInstance().getApplicationContext()) + "/project/index.php?action=render&title=" + encodedTitle; Response response = connect(fullUrl, retry); Document doc = response.parse(); /* * Add your section of alternative language here, create own * parser for each language for modularity reason */ if (!page.getLanguage().equals(Constants.LANG_ENGLISH)) novel = BakaTsukiParserAlternative.ParseNovelDetails(doc, page); else novel = BakaTsukiParser.ParseNovelDetails(doc, page); } catch (HttpStatusException hex) { ++retry; if (notifier != null) { String message = context.getResources().getString(R.string.load_novel_retry, retry, getRetry(), hex.getMessage()); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } if (hex.getStatusCode() == 404) { Log.w(TAG, "Page not Found (404) for: " + page.getPage()); page.setMissing(true); throw hex; } if (retry >= getRetry()) throw hex; } catch (EOFException eof) { ++retry; if (notifier != null) { String message = context.getResources().getString(R.string.load_novel_retry, retry, getRetry(), eof.getMessage()); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } if (retry >= getRetry()) throw eof; } catch (IOException eof) { ++retry; String message = context.getResources().getString(R.string.load_novel_retry, retry, getRetry(), eof.getMessage()); if (notifier != null) { notifier.onProgressCallback(new CallbackEventData(message, TAG)); } Log.d(TAG, message, eof); if (retry >= getRetry()) throw eof; } // Novel details' Page Model if (novel != null) { page = updateNovelDetailsPageModel(page, notifier, novel); // download cover image if (novel.getCoverUrl() != null) { if (notifier != null) { String message = context.getResources().getString(R.string.load_novel_cover_image); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } DownloadFileTask task = new DownloadFileTask(novel.getCoverUrl(), page.getPage(), notifier); ImageModel image = task.downloadImage(); Log.d(TAG, "Cover Image: " + image.toString() + " path: " + image.getPath()); // update cover filepath novel.setCover(image.getName()); } synchronized (dbh) { // insert to DB and get saved value SQLiteDatabase db = dbh.getWritableDatabase(); try { db.beginTransaction(); novel = NovelCollectionModelHelper.insertNovelDetails(dbh, db, novel); db.setTransactionSuccessful(); } finally { db.endTransaction(); db.close(); } } // update info for each chapters if (notifier != null) { String message = context.getResources().getString(R.string.load_novel_chapters_fetch, page.getPage()); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } ArrayList<PageModel> chapters = getUpdateInfo(novel.getFlattedChapterList(), notifier); if (notifier != null) { String message = context.getResources().getString(R.string.load_novel_chapters_saving); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } for (PageModel pageModel : chapters) { if (pageModel.getPage().endsWith("&action=edit&redlink=1")) { pageModel.setMissing(true); } pageModel = updatePageModel(pageModel); } Log.d(TAG, "Complete getting Novel Details from internet: " + page.getPage()); break; } } return novel; } public PageModel updateNovelDetailsPageModel(PageModel page, ICallbackNotifier notifier, NovelCollectionModel novel) throws Exception { if (notifier != null) { String message = context.getResources().getString(R.string.load_novel_details_update, page.getPage()); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } PageModel novelPageTemp = getPageModelFromInternet(page, notifier); if (novelPageTemp != null) { page = getUpdateInfo(page, notifier); page.setLastUpdate(novelPageTemp.getLastUpdate()); page.setLastCheck(new Date()); novel.setLastUpdate(novelPageTemp.getLastUpdate()); novel.setLastCheck(new Date()); } else { page.setLastUpdate(new Date(0)); page.setLastCheck(new Date()); novel.setLastUpdate(new Date(0)); novel.setLastCheck(new Date()); } // save the changes synchronized (dbh) { SQLiteDatabase db = dbh.getWritableDatabase(); try { page = PageModelHelper.insertOrUpdatePageModel(dbh, db, page, true); } finally { db.close(); } } return page; } public int deleteBooks(BookModel bookDel) { synchronized (dbh) { // get from db SQLiteDatabase db = dbh.getReadableDatabase(); try { BookModel tempBook = null; if (bookDel.getId() > 0) { tempBook = BookModelHelper.getBookModel(dbh, db, bookDel.getId()); } else if (!Util.isStringNullOrEmpty(bookDel.getPage()) && !Util.isStringNullOrEmpty(bookDel.getTitle())) { tempBook = BookModelHelper.getBookModel(dbh, db, bookDel.getPage(), bookDel.getTitle()); } else { Log.e(TAG, "DELETE BOOK failure: " + bookDel.getId() + " Page:" + bookDel.getPage() + " Title:" + bookDel.getTitle()); } if (tempBook != null) { return BookModelHelper.deleteBookModel(dbh, db, tempBook); } } finally { db.close(); } } return 0; } public ArrayList<PageModel> getChapterCollection(String page, String title, BookModel book) { synchronized (dbh) { // get from db SQLiteDatabase db = dbh.getReadableDatabase(); try { return NovelCollectionModelHelper.getChapterCollection(dbh, db, page + Constants.NOVEL_BOOK_DIVIDER + title, book); } finally { db.close(); } } } public void deleteBookCache(BookModel bookDel) { for (PageModel p : bookDel.getChapterCollection()) { deleteChapterCache(p); } } // endregion // region Novel Contents public ArrayList<PageModel> getAllContentPageModel() { ArrayList<PageModel> result = null; synchronized (dbh) { // get from db SQLiteDatabase db = dbh.getReadableDatabase(); try { result = PageModelHelper.getAllContentPageModel(dbh, db); } finally { db.close(); } } return result; } public NovelContentModel getNovelContent(PageModel page, boolean download, ICallbackNotifier notifier) throws Exception { NovelContentModel content = null; synchronized (dbh) { // get from db SQLiteDatabase db = dbh.getReadableDatabase(); try { content = NovelContentModelHelper.getNovelContent(dbh, db, page.getPage()); } finally { db.close(); } } // get from Internet; if (download && content == null) { Log.d("getNovelContent", "Get from Internet: " + page.getPage()); content = getNovelContentFromInternet(page, notifier); } return content; } public NovelContentModel getNovelContentFromInternet(PageModel page, ICallbackNotifier notifier) throws Exception { checkInternetConnection(); String oldTitle = page.getTitle(); NovelContentModel content = new NovelContentModel(); int retry = 0; Document doc = null; while (retry < getRetry()) { try { String encodedUrl = String.format(Constants.API_URL_CONTENT, UIHelper.getBaseUrl(LNReaderApplication.getInstance().getApplicationContext()), Util.UrlEncode(page.getPage())); if (!page.getPage().endsWith(Constants.API_REDLINK)) { Response response = connect(encodedUrl, retry); doc = response.parse(); page.setMissing(false); } else { Log.w(TAG, "redlink page: " + page.getPage()); String titleClean = page.getPage().replace(Constants.API_REDLINK, ""); doc = Jsoup.parse("<div class=\"noarticletext\">" + "<p>There is currently no text in this page." + "You can <a href=\"/project/index.php?title=Special:Search/" + titleClean + "\" title=\"Special:Search/" + titleClean + "\">search for this page title</a> in other pages," + "or <span class=\"plainlinks\"><a rel=\"nofollow\" class=\"external text\" href=\""+ Constants.ROOT_HTTPS + Constants.ROOT_URL + "/project/index.php?title=Special:Log&page=" + titleClean + "\">search the related logs</a></span>." + "</p>" + "</div>"); page.setMissing(true); } content = BakaTsukiParser.ParseNovelContent(doc, page); content.setUpdatingFromInternet(true); break; } catch (EOFException eof) { ++retry; if (notifier != null) { String message = context.getResources().getString(R.string.load_novel_retry, retry, getRetry(), eof.getMessage()); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } if (retry >= getRetry()) throw eof; } catch (IOException eof) { ++retry; String message = context.getResources().getString(R.string.load_novel_retry, retry, getRetry(), eof.getMessage()); if (notifier != null) { notifier.onProgressCallback(new CallbackEventData(message, TAG)); } Log.d(TAG, message, eof); if (retry >= getRetry()) throw eof; } } if (doc != null) { // download all attached images for (ImageModel image : content.getImages()) { if (notifier != null) { String message = context.getResources().getString(R.string.load_novel_image_download, image.getUrl()); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } DownloadFileTask task = new DownloadFileTask(image.getUrl(), page.getPage(), notifier); image = task.downloadImage(); } // download linked big images boolean isDownloadBigImage = PreferenceManager.getDefaultSharedPreferences(LNReaderApplication.getInstance()).getBoolean(Constants.PREF_DOWLOAD_BIG_IMAGE, false); if (isDownloadBigImage) { Document imageDoc = Jsoup.parse(content.getContent()); ArrayList<String> images = CommonParser.parseImagesFromContentPage(imageDoc); for (String imageUrl : images) { ImageModel bigImage = new ImageModel(); bigImage.setBigImage(true); bigImage.setName(imageUrl); bigImage.setReferer(imageUrl); bigImage = getImageModel(bigImage, page.getPage(), notifier); // Save image to DB with it page as parent. bigImage.setParent(page.getPage()); this.insertImage(bigImage); } } // get last updated info PageModel contentPageModelTemp = getPageModelFromInternet(content.getPageModel(), notifier); if (contentPageModelTemp != null) { // overwrite the old title content.getPageModel().setTitle(oldTitle); // syncronize the date content.getPageModel().setLastUpdate(contentPageModelTemp.getLastUpdate()); content.getPageModel().setLastCheck(new Date()); content.setLastUpdate(contentPageModelTemp.getLastUpdate()); content.setLastCheck(new Date()); } // page model will be also saved in insertNovelContent() synchronized (dbh) { // save to DB, and get the saved value SQLiteDatabase db = dbh.getWritableDatabase(); try { // TODO: somehow using transaction cause problem... db.beginTransaction(); content = NovelContentModelHelper.insertNovelContent(dbh, db, content, page, false); db.setTransactionSuccessful(); } finally { db.endTransaction(); db.close(); } } } return content; } public NovelContentUserModel getNovelContentUserModel(String page, ICallbackNotifier notifier) throws Exception { NovelContentUserModel content = null; synchronized (dbh) { // get from db SQLiteDatabase db = dbh.getReadableDatabase(); try { content = NovelContentUserHelperModel.getNovelContentUserModel(dbh, db, page); } finally { db.close(); } } return content; } public NovelContentUserModel updateNovelContentUserModel(NovelContentUserModel page, ICallbackNotifier notifier) throws Exception { if (notifier != null) { String message = "Updating user data: " + page.getPage(); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } // save the changes synchronized (dbh) { SQLiteDatabase db = dbh.getWritableDatabase(); try { page = NovelContentUserHelperModel.insertModel(dbh, db, page); } finally { db.close(); } } return page; } public NovelContentModel updateNovelContent(NovelContentModel content, boolean forceUpdateContent) throws Exception { synchronized (dbh) { SQLiteDatabase db = dbh.getWritableDatabase(); try { content = NovelContentModelHelper.insertNovelContent(dbh, db, content, content.getPageModel(), forceUpdateContent); } finally { db.close(); } } return content; } public int deleteNovelContent(PageModel ref) { int result = 0; synchronized (dbh) { SQLiteDatabase db = dbh.getWritableDatabase(); try { result = NovelContentModelHelper.deleteNovelContent(dbh, db, ref); deleteImageByParent(ref.getPage()); } finally { db.close(); } } return result; } public void deleteChapterCache(PageModel chapter) { deleteNovelContent(chapter); deleteImageByParent(chapter.getPage()); // Set isDownloaded to false chapter.setDownloaded(false); updatePageModel(chapter); } // endregion // region ImageModel /** * Get image from db, if not exist will try to download from internet * * @param image * @param notifier * @return * @throws Exception */ public ImageModel getImageModel(ImageModel image, String parent, ICallbackNotifier notifier) throws Exception { if (image == null || image.getName() == null) throw new BakaReaderException("Empty Image!", BakaReaderException.EMPTY_IMAGE); ImageModel imageTemp = null; synchronized (dbh) { SQLiteDatabase db = dbh.getReadableDatabase(); try { imageTemp = ImageModelHelper.getImage(dbh, db, image); if (imageTemp == null) { if (image.getReferer() == null) image.setReferer(image.getName()); Log.d(TAG, "Image not found, might need to check by referer: " + image.getName() + ", referer: " + image.getReferer()); imageTemp = ImageModelHelper.getImageByReferer(dbh, db, image); } } finally { db.close(); } } boolean downloadBigImage = false; if (imageTemp == null) { Log.i(TAG, "Image not found in DB, getting data from internet: " + image.getName()); downloadBigImage = true; } else if (!new File(imageTemp.getPath()).exists()) { try { Log.i(TAG, "Image found in DB, but doesn't exist in path: " + imageTemp.getPath() + "\nAttempting URLDecoding method with default charset:" + java.nio.charset.Charset.defaultCharset().displayName()); if (!new File(java.net.URLDecoder.decode(imageTemp.getPath(), java.nio.charset.Charset.defaultCharset().displayName())).exists()) { Log.i(TAG, "Image found in DB, but doesn't exist in URL decoded path: " + java.net.URLDecoder.decode(imageTemp.getPath(), java.nio.charset.Charset.defaultCharset().displayName())); downloadBigImage = true; } // else Log.i(TAG, "Image found in DB with URL decoded path: " + // java.net.URLDecoder.decode(imageTemp.getPath(), // java.nio.charset.Charset.defaultCharset().displayName())); } catch (Exception e) { Log.i(TAG, "Image found in DB, but path string seems to be broken: " + imageTemp.getPath() + " Charset:" + java.nio.charset.Charset.defaultCharset().displayName()); downloadBigImage = true; } } if (downloadBigImage) { Log.d(TAG, "Downloading big image from internet: " + image.getName()); imageTemp = getImageModelFromInternet(image, parent, notifier); } return imageTemp; } /** * Get image from internet from File:xxx * * @param image * @param notifier * @return * @throws Exception */ public ImageModel getImageModelFromInternet(ImageModel image, String parent, ICallbackNotifier notifier) throws Exception { checkInternetConnection(); String url = image.getName(); if (!url.startsWith("http")) url = UIHelper.getBaseUrl(LNReaderApplication.getInstance().getApplicationContext()) + url; if (notifier != null) { String message = context.getResources().getString(R.string.load_novel_image_fetch, url); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } int retry = 0; while (retry < getRetry()) { try { Response response = connect(url, retry); Document doc = response.parse(); // only return the full image url image = CommonParser.parseImagePage(doc); DownloadFileTask downloader = new DownloadFileTask(image.getUrl(), parent, notifier); image = downloader.downloadImage(); image.setReferer(url); image = insertImage(image); break; } catch (EOFException eof) { if (notifier != null) { String message = context.getResources().getString(R.string.load_novel_retry, retry, getRetry(), eof.getMessage()); notifier.onProgressCallback(new CallbackEventData(message, TAG)); } ++retry; if (retry >= getRetry()) throw eof; } } return image; } public ImageModel insertImage(ImageModel image) { synchronized (dbh) { // save to db and get the saved value SQLiteDatabase db = dbh.getWritableDatabase(); try { image = ImageModelHelper.insertImage(dbh, db, image); } finally { db.close(); } } return image; } public ArrayList<ImageModel> getAllImages() { ArrayList<ImageModel> result; synchronized (dbh) { SQLiteDatabase db = dbh.getReadableDatabase(); result = ImageModelHelper.getAllImages(dbh, db); } return result; } public int deleteImageByParent(String parent) { int total = 0; synchronized (dbh) { SQLiteDatabase db = dbh.getWritableDatabase(); try { total = ImageModelHelper.deleteImageByParent(dbh, db, parent); } finally { db.close(); } } return total; } // endregion // region bookmarks public ArrayList<BookmarkModel> getBookmarks(PageModel novel) { ArrayList<BookmarkModel> bookmarks = new ArrayList<BookmarkModel>(); synchronized (dbh) { SQLiteDatabase db = dbh.getReadableDatabase(); bookmarks = BookmarkModelHelper.getBookmarks(dbh, db, novel); } return bookmarks; } public int addBookmark(BookmarkModel bookmark) { synchronized (dbh) { SQLiteDatabase db = dbh.getWritableDatabase(); return BookmarkModelHelper.insertBookmark(dbh, db, bookmark); } } public int deleteBookmark(BookmarkModel bookmark) { synchronized (dbh) { SQLiteDatabase db = dbh.getWritableDatabase(); return BookmarkModelHelper.deleteBookmark(dbh, db, bookmark); } } public ArrayList<BookmarkModel> getAllBookmarks(boolean isOrderByDate) { ArrayList<BookmarkModel> bookmarks = new ArrayList<BookmarkModel>(); synchronized (dbh) { SQLiteDatabase db = dbh.getReadableDatabase(); bookmarks = BookmarkModelHelper.getAllBookmarks(dbh, db, isOrderByDate); } return bookmarks; } // endregion // region updates public boolean isContentUpdated(PageModel page) { synchronized (dbh) { SQLiteDatabase db = dbh.getReadableDatabase(); return dbh.isContentUpdated(db, page); } } public int isNovelUpdated(PageModel page) { synchronized (dbh) { SQLiteDatabase db = dbh.getReadableDatabase(); return dbh.isNovelUpdated(db, page); } } public ArrayList<UpdateInfoModel> getAllUpdateHistory() { synchronized (dbh) { SQLiteDatabase db = dbh.getReadableDatabase(); ArrayList<UpdateInfoModel> updates = UpdateInfoModelHelper.getAllUpdateHistory(dbh, db); for (UpdateInfoModel update : updates) { try { update.getUpdatePageModel(); } catch (Exception e) { Log.e(TAG, "Unable to get pagemodel for: " + update.getUpdatePage(), e); } } return updates; } } public void deleteAllUpdateHistory() { synchronized (dbh) { SQLiteDatabase db = dbh.getWritableDatabase(); UpdateInfoModelHelper.deleteAllUpdateHistory(db); } } public void deleteUpdateHistory(UpdateInfoModel updateInfo) { synchronized (dbh) { SQLiteDatabase db = dbh.getWritableDatabase(); UpdateInfoModelHelper.deleteUpdateHistory(dbh, db, updateInfo); } } public void insertUpdateHistory(UpdateInfoModel update) { synchronized (dbh) { SQLiteDatabase db = dbh.getWritableDatabase(); UpdateInfoModelHelper.insertUpdateHistory(dbh, db, update); } } // endregion // region others public ArrayList<PageModel> doSearch(String searchStr, boolean isNovelOnly, ArrayList<String> languageList) { if (searchStr == null || searchStr.length() < 3) return null; ArrayList<PageModel> result; synchronized (dbh) { SQLiteDatabase db = dbh.getReadableDatabase(); result = dbh.doSearch(db, searchStr, isNovelOnly, languageList); } return result; } public void resetZoomLevel(ICallbackNotifier notifier) throws Exception { if (notifier != null) { String message = "Resetting default zoom level"; notifier.onProgressCallback(new CallbackEventData(message, TAG)); } // save the changes synchronized (dbh) { SQLiteDatabase db = dbh.getWritableDatabase(); try { int result = NovelContentUserHelperModel.resetZoomLevel(dbh, db); Log.d(TAG, "Affected: " + result); } finally { db.close(); } } } // endregion // region private methods private int getRetry() { return UIHelper.getIntFromPreferences(Constants.PREF_RETRY, 3); } private int getTimeout(int retry) { boolean increaseRetry = PreferenceManager.getDefaultSharedPreferences(LNReaderApplication.getInstance().getApplicationContext()).getBoolean(Constants.PREF_INCREASE_RETRY, false); int timeout = UIHelper.getIntFromPreferences(Constants.PREF_TIMEOUT, 60) * 1000; if (increaseRetry) { timeout = timeout * (retry + 1); } return timeout; } /** * Get if need to use internal app keystore. * * @return */ private boolean getUseAppKeystore() { boolean result = UIHelper.getUseAppKeystore(LNReaderApplication.getInstance().getApplicationContext()); if (result) { Log.d(TAG, "Using app keystore"); } return result; } private boolean isQuickLoad() { return UIHelper.getQuickLoad(LNReaderApplication.getInstance().getApplicationContext()); } private boolean isAlphabeticalOrder() { return UIHelper.isAlphabeticalOrder(LNReaderApplication.getInstance().getApplicationContext()); } // endregion // region storage public ArrayList<FindMissingModel> getMissingItems(String extra) { ArrayList<FindMissingModel> list = null; if (extra.equalsIgnoreCase(Constants.PREF_REDLINK_CHAPTER)) { synchronized (dbh) { SQLiteDatabase db = dbh.getReadableDatabase(); list = FindMissingModelHelper.getAllRedlinkChapter(dbh, db); } } else if (extra.equalsIgnoreCase(Constants.PREF_MISSING_CHAPTER)) { synchronized (dbh) { SQLiteDatabase db = dbh.getReadableDatabase(); list = FindMissingModelHelper.getAllMissingChapter(dbh, db); } } else if (extra.equalsIgnoreCase(Constants.PREF_EMPTY_BOOK)) { synchronized (dbh) { SQLiteDatabase db = dbh.getReadableDatabase(); list = FindMissingModelHelper.getAllEmptyBook(dbh, db); } } else if (extra.equalsIgnoreCase(Constants.PREF_EMPTY_NOVEL)) { synchronized (dbh) { SQLiteDatabase db = dbh.getReadableDatabase(); list = FindMissingModelHelper.getAllEmptyNovel(dbh, db); } } else { list = new ArrayList<FindMissingModel>(); FindMissingModel dummy = new FindMissingModel(); dummy.setTitle("Dummy Title: " + extra); dummy.setDetails("Dummy Details"); list.add(dummy); } return list; } public int deleteMissingItem(FindMissingModel missing, String mode) { if (mode.equalsIgnoreCase(Constants.PREF_REDLINK_CHAPTER)) { return deletePage(new PageModel(missing.getPage())); } else if (mode.equalsIgnoreCase(Constants.PREF_MISSING_CHAPTER)) { return deletePage(new PageModel(missing.getPage())); } else if (mode.equalsIgnoreCase(Constants.PREF_EMPTY_BOOK)) { BookModel book = new BookModel(); book.setId(missing.getId()); book.setPage(missing.getPage()); book.setTitle(missing.getDetails()); book.setId(missing.getId()); // Log.e(TAG, "DELETE BOOK: " + book.getId() + " Page:" + book.getPage() + " Title:" + book.getTitle()); return deleteBooks(book); } else if (mode.equalsIgnoreCase(Constants.PREF_EMPTY_NOVEL)) { return deleteNovel(new PageModel(missing.getPage())); } return 0; } // endregion // region network private void checkInternetConnection() throws BakaReaderException { if (!LNReaderApplication.getInstance().isOnline()) throw new BakaReaderException("OFFLINE (No Internet Connection)", BakaReaderException.OFFLINE); } private Response connect(String url, int retry) throws IOException { // allow to use its keystore. return Jsoup.connect(url).validateTLSCertificates(!getUseAppKeystore()).timeout(getTimeout(retry)).execute(); } // endregion }